# PCA函数
import numpy as np
from sklearn.decomposition import PCA
from scipy.stats import zscore
import pandas as pd
import DataTranslate
import matplotlib.pylab as plt
import Draw

def PCA_S(datainput:pd.DataFrame='输入的数据', excelpath='excel数据地址', PCAnums='提取主成分个数', KeepExcel=True):
    """
    1、PCA时线性提取特征值,降维,去噪的算法
    2、建议使用输入的data，DataFrame
    3、默认行为样本,列为样本指标
    :param datainput:输入的矩阵数据DataFrame,没有填excelpath
    :param excelpath:输入的excel地址,没有填datainput
    :param PCAnums:提取的前个主成分,第一次执行的时候可以先不提取，看贡献率怎么样
    :param KeepExcel:是否保存PCA结果.xlsx
    :return:
    1、xs1:主成分的系数
    2、score:每个样本对于每个主成分的得分
    3、Translated_values：特征向量转换之后的矩阵数据
    4、Contribution_rate_cumsum：特征值的累计贡献，可以用于画图
    5、Contribution_rate：贡献率
        其中行是主成分,列是样本
        若要取前两个主成分的得分
        score.values[:2,:]即可
    """
    print('\n*****************************PCA程序开始执行*****************************')
    data = []
    if excelpath != 'excel数据地址':
        data = pd.read_excel(excelpath)
    elif type(datainput) == pd.DataFrame:
        data = datainput
    else:
        print('没有输入数据!!!')
        return
    # 使用主成分分析PCA
    # 库定义的函数
    data = zscore(data, ddof=1)
    # 两个函数的作用是一样的
    # 拟合PCA
    print('拟合PCA......')
    model = PCA().fit(data)
    # print(Data_Frame == b)
    # print('特征值为:', model.explained_variance_)
    # 贡献率
    Contribution_rate=model.explained_variance_ratio_
    # 计算累计贡献率
    Contribution_rate_cumsum=np.cumsum(Contribution_rate)
    # print('PCA各主成分贡献率:')
    # 归一化累计贡献率
    # 提出各主成分系数，每行是一个主成分
    print('提取各主成分系数......')
    xs1 = model.components_
    # 计算各个主成分系数的和
    print('计算各个主成分系数的和......')
    check = xs1.sum(axis=1, keepdims=True)
    if KeepExcel:
        # 参考
        # https://blog.csdn.net/m0_47384542/article/details/109647491
        # https://blog.csdn.net/qq_28808697/article/details/105563008
        # 保存结果到excel：PCA系数.xlsx
        # 默认保存到当前路径下面
        # 这里也可以是完全路径
        print('正在保存到' + 'PCA结果.xlsx......')
        writer = pd.ExcelWriter(r'PCA结果.xlsx')
        pd.DataFrame(xs1).to_excel(writer, sheet_name='PCA系数')
        pd.DataFrame(model.explained_variance_).to_excel(writer, sheet_name='特征值')
        pd.DataFrame(model.explained_variance_ratio_).to_excel(writer, sheet_name='PCA各主成分贡献率')
        pd.DataFrame(check).to_excel(writer, sheet_name='各个主成分系数的和')
        # 记得保存写入流
        writer.save()
        print('PCA结果.xlsx保存成功!!!')
    # 计算主成分得分
    # 若取两个主成分，可用于聚类可视化
    # 如果指定了取几个主成分,直接计算后返回
    if type(PCAnums) == int:
        # 绘制主成分系数图，用于分析指标重要性
        # plt.figure()
        # factors = np.array(xs1[:PCAnums, :])
        # for i in range(PCAnums):
        #     plt.plot(factors[i, :], label='第' + str(i + 1) + '主成分')
        # plt.legend()
        # plt.title('主成分系数图')
        # if PCAnums != 0:
        #     # 计算主成分得分
        #     score = xs1[:PCAnums, :] @ data.T
        #     print('主成分得分:\n', score)

        Translated_values=np.dot(data,xs1[:PCAnums,:])
    # 计算主成分转换后的矩阵
    # xs1特征向量
    Translated_values=np.dot(data,xs1)
    # score 用于pca排名的分数
    score = xs1 @ data.T
    return xs1, score,Translated_values,Contribution_rate_cumsum,Contribution_rate
    print('\n*****************************PCA程序执行成功*****************************')


# 主成分聚类图
# 用于当完成主成分和聚类分析后绘制二维图使用

# 参数解释
# data:原数据,建议先去掉无用指标,如ID
# Centersnums:聚类的中心数
# xs1：主成分系数,其实只会取前两行
# Centers:聚类的几何中心,默认的是欧几里得聚类
# ClusterResult:聚类的结果
def PCA_cluser(data, Centersnums, xs1, Centers, ClusterResult):
    # 计算主成分得分
    score = xs1[:2, :] @ data
    score = np.array(score)
    # 计算聚类中心
    ClusterCenter = xs1[:2, :] @ Centers.T

    plt.figure()
    Colors = Draw.GetRandomColors(len(Centers))
    # 绘制样本到其聚类中心的实线
    # plt.plot(score[0, :], score[1, :], 'o')
    for i in range(len(ClusterResult)):
        plt.plot(score[0, i], score[1, i], 'o',color=Colors[ClusterResult[i]-1])
        plt.plot(
            # 绘制直线
            # x
            [score[0, i], ClusterCenter[0, ClusterResult[i] - 1]],
            # y
            [score[1, i], ClusterCenter[1, ClusterResult[i] - 1]], '-',color=Colors[ClusterResult[i]-1]
        )
    # 绘制聚类中心
    for i in range(Centersnums):
        plt.plot(ClusterCenter[0, i], ClusterCenter[1, i], 'o',color=Colors[i], label='第' + str(i + 1) + '聚类中心')

    # 添加图例
    plt.legend()
    plt.title('聚类二维图')

# ==========================================================================
# KPCA
# n = data_update.shape[0];
# c=Data_Frame
# K = (1 + c @ (c.T)) ** 3
# J = np.ones((n, n)) / n;
# Kw = K - (K @ J) - (J @ K) + (J @ K @ J)
# val, vec = np.linalg.eig(Kw)
# pd.DataFrame(vec).to_excel('12.4题KPCA系数.xlsx')
# print('KPCA特征值为：', val)
# r2 = val / sum(val)  # 提取各主成分的贡献率
# print('KPCA各主成分贡献率：', np.round(r2,4))
# print('KPCA主成分系数：\n', np.round(vec, 4))
# print('KPCA累积贡献率：', np.cumsum(r2))
# n2 = 4  # 选取主成分的个数
# m1 = vec.max(axis=0)  # 求每列的最大值
# m2 = vec.min(axis=0)  # 求每列的最小值
# sgn = (-1) * (abs(m2) > m1) + (abs(m2) <= m1)  # 构造±1的向量
# vec = vec * sgn  # 修改特征向量的符号
# df2 = Kw @ (vec[:, :n2])  # 计算主成分得分
# g2 = df2 @ r2[:n2]  # 计算综合评价得分
# # print('核主成分评价得分：\n', np.round(g2, 4))
#
# g2=np.array(g2)
# px3= abs(g2.argsort()-51)+1
# Compare['KPCA排序']=px3
#
# # ==========================================================================
# # 因子分析
# d=Data_Frame
# r=np.corrcoef(d.T)          #求相关系数矩阵
# val,vec=np.linalg.eig(r)
# cs=np.cumsum(val)  #求特征值的累加和
# rate=val/cs[-1]    #求贡献率
# srate=sorted(rate,reverse=True)
# print('因子分析特征值为：\n',val,'\n贡献率为：\n',srate)
# # 取两个因子
# fa = FA(2,rotation='varimax')  #构建模型
# fa.fit(d)         #求解方差最大的模型
# A=fa.loadings_    #提取载荷矩阵
# gx=np.sum(A**2, axis=0)    #计算信息贡献
# s2=1-np.sum(A**2, axis=1)  #计算特殊方差
# ss=np.linalg.inv(np.diag(s2))
# f=ss@A@np.linalg.inv(A.T@ss@A)  #计算因子得分函数系数
# df=d@f            #计算因子得分
# pj=df@gx/sum(gx)  #计算评价值
#
# pj=np.array(pj)
# px4= abs(pj.argsort()-51)+1
# Compare['因子分析排序']=px4
#
# print('排序结果:\n',Compare)
# # np.array().reshape()
# # A=A.reshape([4,2])
# Compare.to_excel('12.4题排序结果.xlsx',sheet_name='Sheet1')
# pd.DataFrame(A).to_excel('12.4题因子分析载荷矩阵系数.xlsx')
